package com.metroinfo.workers.selector;

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

import com.metroinfo.infrastructure.MainDataStructure;
import com.metroinfo.kernel.ProcessData;
import com.metroinfo.kernel.data.ResponseData;
import com.metroinfo.parser.CommandParser;
import com.metroinfo.parser.GeneralSettings;
import com.metroinfo.parser.data.GeneralRequest;
import com.metroinfo.workers.ChannelIO;

/**
 * Primary driver class used by non-blocking Servers to receive, prepare, send,
 * and shutdown requests.
 * 
 * @author Mark Reinhold
 * @author Brad R. Wetmore
 * @version 1.4, 10/01/12
 */
class RequestHandler implements Handler {

	private ChannelIO cio;
	private ByteBuffer rbb = null;

	private boolean requestReceived = false;
	private ByteBuffer request = ByteBuffer
			.allocate(GeneralSettings.RECEIVE_DATA_SIZE);
	private Reply reply = null;

	private MainDataStructure mds;

	private static int created = 0;

	RequestHandler(ChannelIO cio, MainDataStructure mmds) {
		this.cio = cio;
		mds = mmds;
	}

	// Returns true when request is complete
	// May expand rbb if more room required
	//
	private boolean receive(SelectionKey sk) throws IOException {
		ByteBuffer tmp = null;

		if (requestReceived) {
			return true;
		}

		/*
		 * if (!cio.doHandshake(sk)) { return false; }
		 */
		if ((cio.read() < 0) || isReceived(cio.getReadBuf())) {
			rbb = cio.getReadBuf();
			return (requestReceived = true);
		}
		return false;
	}

	private boolean isReceived(ByteBuffer bb) {
		return bb.get(bb.position()) == 0;

	}

	// When parse is successfull, saves request and returns true
	//
	private boolean parse() {
		request = rbb;
		return true;

	}

	// Ensures that reply field is non-null
	//
	private void build() throws IOException {
/*		Request.Action action = request.action();
		if ((action != Request.Action.GET) && (action != Request.Action.HEAD)) {
			reply = new Reply(Reply.Code.METHOD_NOT_ALLOWED, new StringContent(
					request.toString()));
		}
		reply = new Reply(Reply.Code.OK, new FileContent(request.uri()), action);
*/
		CommandParser comParser = new CommandParser();
		GeneralRequest generalReq = comParser.getData(rbb);
		
		ProcessData processData = new ProcessData(mds);
		ResponseData responseData = processData.process(generalReq);
		
		switch (generalReq.getReqType()){
		case(GeneralSettings.CT_SEND_DATA):
//			cio.write(responseData.getResponse());
			reply = new Reply(new StringContent(responseData.getResponse()));
			break;
		default:
			break;
		}
	
	}

	public void handle(SelectionKey sk) throws IOException {
		try {

			if (request == null) {
				if (!receive(sk))
					return;
				rbb.flip();
				if (parse())
					build();
				try {
					reply.prepare();
				} catch (IOException x) {
					reply.release();
				/*	reply = new Reply(Reply.Code.NOT_FOUND,
							new StringContent(x));
				*/	reply.prepare();
				}
				if (send()) {
					// More bytes remain to be written
					sk.interestOps(SelectionKey.OP_WRITE);
				} else {
					// Reply completely written; we're done
					if (cio.shutdown()) {
						cio.close();
						reply.release();
					}
				}
			} else {
				if (!send()) { // Should be rp.send()
					if (cio.shutdown()) {
						cio.close();
						reply.release();
					}
				}
			}
		} catch (IOException x) {
			String m = x.getMessage();
			if (!m.equals("Broken pipe")
					&& !m.equals("Connection reset by peer")) {
				System.err.println("RequestHandler: " + x.toString());
			}

			try {
				/*
				 * We had a failure here, so we'll try to be nice before closing
				 * down and send off a close_notify, but if we can't get the
				 * message off with one try, we'll just shutdown.
				 */
				cio.shutdown();
			} catch (IOException e) {
				// ignore
			}

			cio.close();
			if (reply != null) {
				reply.release();
			}
		}

	}

	private boolean send() throws IOException {
		try {
			return reply.send(cio);
		} catch (IOException x) {
			if (x.getMessage().startsWith("Resource temporarily")) {
				System.err.println("## RTA");
				return true;
			}
			throw x;
		}
	}
}
